﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Microsoft.FxCop.Sdk;

namespace FxCopContrib.TestHarness
{
    internal class FxCopRunner
    {
        private static object syncRoot = new object();
        private static bool initialized = false;

        private BaseIntrospectionRule rule;
        private AssemblyUnderTest assembly;

        internal FxCopRunner(BaseIntrospectionRule ruleToRun, AssemblyUnderTest onAssembly)
        {
            if (ruleToRun == null)
            {
                throw new ArgumentNullException("ruleToRun");
            }

            if (onAssembly == null)
            {
                throw new ArgumentNullException("onAssembly");
            }

            rule = ruleToRun;
            assembly = onAssembly;
        }

        private static void InitializeEngine()
        {
            if (!initialized)
            {
                lock (syncRoot)
                {
                    if (!initialized)
                    {
                        initialized = true;
                    }
                }
            }
        }

        private static BindingFlags defaultFlags = System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public;
        public void Run()
        {
            AssemblyNode module = assembly.GetAssemblyNode();

            InitializeEngine();

            rule.BeforeAnalysis();
            CheckModule(module);
            CheckTypes(module);
            CheckResources(module);
            rule.AfterAnalysis();
        }

        private void CheckModule(AssemblyNode module)
        {
            if (ShouldCheckModule)
                rule.Check(module);
        }

        private bool ShouldCheckModule
        {
            get
            {
                return (rule.GetType().GetMethod("Check", defaultFlags, null, new System.Type[] { typeof(AssemblyNode) }, new ParameterModifier[] { }) != null);
            }
        }

        private void CheckResources(AssemblyNode module)
        {
            if (ShouldCheckResources)
            {
                foreach (var resource in module.Resources)
                {
                    rule.Check(resource);
                }
            }
        }

        private bool ShouldCheckResources
        {
            get
            {
                return (rule.GetType().GetMethod("Check", defaultFlags, null, new System.Type[] { typeof(Resource) }, new ParameterModifier[] { }) != null);
            }
        }

        private void CheckTypes(AssemblyNode module)
        {
            if (ShouldCheckTypeNode)
            {
                foreach (var type in module.Types)
                {
                    CheckType(type);
                }
            }
        }

        private void CheckType(TypeNode type)
        {
            
            rule.Check(type);


            CheckMembers(type);
        }

        private bool ShouldCheckTypeNode
        {
            get
            {
                return (rule.GetType().GetMethod("Check", defaultFlags, null, new System.Type[] { typeof(TypeNode) }, new ParameterModifier[] { }) != null);
            }
        }

        private void CheckMembers(TypeNode type)
        {
            if (ShouldCheckMembers)
            {
                foreach (var member in type.Members)
                {
                    CheckMember(member);
                }
            }
        }

        private void CheckMember(Member member)
        {
            rule.Check(member);

            CheckParameters(member);
        }

        private bool ShouldCheckParameters
        {
            get
            {
                return (rule.GetType().GetMethod("Check", defaultFlags, null, new System.Type[] { typeof(Parameter) }, new ParameterModifier[] { }) != null);
            }
        }

        private bool ShouldCheckMembers
        {
            get {
                return (rule.GetType().GetMethod("Check", defaultFlags, null, new System.Type[] { typeof(Member) }, new ParameterModifier[] { }) != null);
            }
        }

        private void CheckParameters(Member member)
        {
            Method method = member as Method;

            if (ShouldCheckParameters 
                    && (method != null )
                    && (method.Parameters != null)
                )
            {
                foreach (var parameter in method.Parameters)
                {
                    CheckParameter(parameter);
                }
            }
        }

        private void CheckParameter(Parameter parameter)
        {
            rule.Check(parameter);
        }

    }
}
